home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Disc to the Future 2
/
Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin
/
MAC
/
THINKC
/
3_0
/
MENUDEMO
/
CRUSHMEN.C
next >
Wrap
C/C++ Source or Header
|
1989-02-08
|
13KB
|
478 lines
/*
* crushmenu.c - a custom Menu Definition procedure.
*/
#include <MemoryMgr.h>
#include <Quickdraw.h>
#include <MenuMgr.h>
#include <ToolboxUtil.h>
#define ICONSIDE 32 /* Size (pixels) of one side of an icon */
#define ICON_HMARG 4 /* Margin for left and right of an icon */
#define ICON_VMARG 2 /* Margin for top and bottom of an icon */
#define HSLIDE 6 /* number of pixels each successive entry is shifted left */
#define RMARG 5 /* pixels of blank to the right of the item text */
/*
* The menuData field of the menu's structure
* is a pile of variable-length fields. Basically, it's:
* Pascal string of the Title;
* Pascal string of the first menu item;
* struct iteminfo of the first menu item;
* Pascal string of the second menu item...
* The list is ended by a Pascal string of length 0.
*/
struct iteminfo {
char iconnum; /* encoded Resource ID of the item's icon (or 0) */
char keyequiv; /* its keyboard equivalent (or 0) */
char mark; /* its marking character (or 0) */
Style tstyle; /* its text's style */
};
/*
* struct commons - common information each routine will be interested in.
*
* To save code, the main routine calculates the common information,
* then passes to each subroutine a pointer to the block of information.
*/
struct commons {
FontInfo finfo; /* the dimensions of the System Font */
int lheight; /* the height of one text line */
int cloverwidth; /* the width of the clover ("command") symbol */
int checkwidth; /* the width of the check-mark character */
int numitems; /* the number of items in the menu */
};
/*
* main() - the entry-point to our custom menu definition.
*
* Because the MDEF must be a pure-code resource,
* we can't use any global or static data,
* which forces us to recalculate a lot of information
* each time we're called.
*/
pascal void
main(op, menuh, mrectp, pt, theitemp)
int op; /* the operation to perform */
MenuHandle menuh; /* the menu to operate on */
Rect *mrectp; /* the Rect enclosing the menu (global coordinates) */
Point pt; /* the mouse location (global coordinates) */
int *theitemp; /* the highlighted item */
{
struct commons comm; /* the info of common interest */
char *mcp; /* "Menu Char-Pointer" for traversing the menuData fields */
/*
* Calculate the info we need regardless of the operation.
*/
GetFontInfo(&comm.finfo);
comm.lheight = comm.finfo.ascent + comm.finfo.descent + comm.finfo.leading;
comm.cloverwidth = CharWidth((char) 0x11);
comm.checkwidth = CharWidth((char) 0x12);
comm.numitems = 0;
mcp = (char *) &((*menuh)->menuData);
mcp += ((int) *mcp & 0xFF) + 1; /* skip past the title string */
while (*mcp) {
++comm.numitems;
mcp += ((int) *mcp & 0xFF) + 1 + sizeof(struct iteminfo);
}
/*
* Now do the requested operation.
*/
switch(op) {
case mSizeMsg: /* find the dimensions of the menu */
msize(&comm, menuh);
break;
case mDrawMsg: /* draw the menu */
mdraw(&comm, menuh, mrectp);
break;
case mChooseMsg: /* find and highlight the item the mouse is in */
mchoose(&comm, menuh, mrectp, pt, theitemp);
break;
/* This MDEF doesn't support the popup message, mPopupMsg. */
}
}
/*
* msize() - calculate the dimensions of the given menu,
* placing them in the appropriate fields of the menu structure.
*/
msize(commp, menuh)
struct commons *commp; /* the common information */
MenuHandle menuh;
{
char *mcp; /* "Menu Char-Pointer" for traversing the menuData fields */
int itemnum; /* item number of the current item */
char *name; /* the (Pascal) name of the current item */
struct iteminfo *infop; /* pointer to the current item's info */
int thisheight; /* height of this item */
int totalheight;/* total height of the menu */
int thiswidth; /* width of the current item's text */
int maxright; /* maximum width to the right of the reference point */
maxright = 0;
totalheight = 0;
itemnum = 0;
mcp = (char *) &((*menuh)->menuData);
mcp += ((int) *mcp & 0xFF) + 1;
while (*mcp) {
++itemnum;
name = mcp;
mcp += ((int) *mcp & 0xFF) + 1;
infop = (struct iteminfo *) mcp;
mcp += sizeof(struct iteminfo);
/*
* If we're drawing an icon, our line is that high,
* otherwise, the line is one text-line high.
* (This code assumes that the system font isn't huge.)
*/
if (infop->iconnum) {
thisheight = ICON_VMARG + ICONSIDE + ICON_VMARG;
} else {
thisheight = commp->lheight;
}
/*
* Sum-up the item's width:
* The item contains:
* a check (or other mark) [optional]
* an icon (and its margins) [optional]
* the item's name
* a clover-key equivalent [optional]
* a right-margin
*/
thiswidth = 0;
if (infop->mark) {
thiswidth += CharWidth(infop->mark);
} else {
thiswidth += commp->checkwidth;
}
if (infop->iconnum) {
thiswidth += ICON_HMARG + ICONSIDE + ICON_HMARG;
}
thiswidth += StringWidth(name);
if (infop->keyequiv != '\0') {
thiswidth += commp->cloverwidth * 2 + commp->finfo.widMax;
}
thiswidth += RMARG;
/*
* Adjust the width by the amount this entry is shifted left.
*/
thiswidth -= (itemnum - 1) * HSLIDE;
if (maxright < thiswidth) {
maxright = thiswidth;
}
totalheight += thisheight;
}
(*menuh)->menuHeight = commp->lheight / 2 + totalheight;
(*menuh)->menuWidth = commp->numitems * HSLIDE + maxright;
}
/*
* mdraw() - draw the initial picture of the given menu within the given Rect.
*/
mdraw(commp, menuh, mrectp)
struct commons *commp; /* the common information */
MenuHandle menuh;
Rect *mrectp;
{
char *mcp; /* "Menu Char-Pointer" for traversing the menuData fields */
int itemnum; /* the item number of the current item */
char *name; /* the (Pascal) name of the current item */
struct iteminfo *infop; /* pointer to the current item's info */
Rect itemrect; /* rect enclosing the current item */
Rect barrect; /* rect enclosing the vertical bar for this item */
int textvert; /* vertical position of text in this item */
PenState oldpen;/* the original Pen of the port (to be restored) */
Pattern fuzz; /* pattern to dim with */
int curhorz; /* current horizontal position of the next piece */
Handle iconh; /* handle to the current icon */
Rect iconrect; /* Rect enclosing the icon to draw */
GetPenState(&oldpen);
/*
* Since we can't access the quickdraw global patterns
* (or even our own globals),
* we have to create this pattern each time we're called.
*/
fuzz[0] = (unsigned char) 0xAA;
fuzz[1] = (unsigned char) 0x55;
fuzz[2] = (unsigned char) 0xAA;
fuzz[3] = (unsigned char) 0x55;
fuzz[4] = (unsigned char) 0xAA;
fuzz[5] = (unsigned char) 0x55;
fuzz[6] = (unsigned char) 0xAA;
fuzz[7] = (unsigned char) 0x55;
/* EraseRect(mrectp); The Menu Manager seems to do this for us */
itemrect.top = mrectp->top + commp->lheight / 2;
itemrect.left = mrectp->left + commp->numitems * HSLIDE;
itemrect.right = mrectp->right;
itemnum = 0;
mcp = (char *) &((*menuh)->menuData);
mcp += ((int) *mcp & 0xFF) + 1;
while (*mcp) {
++itemnum;
name = mcp;
mcp += ((int) *mcp & 0xFF) + 1;
infop = (struct iteminfo *) mcp;
mcp += sizeof(struct iteminfo);
/*
* Calculate where in the menu this item goes:
* Its item rectangle, its associated vertical bar,
* and where (vertically) its text goes.
*/
if (infop->iconnum) {
itemrect.bottom = itemrect.top + ICON_VMARG + ICONSIDE + ICON_VMARG;
} else {
itemrect.bottom = itemrect.top + commp->lheight;
}
barrect.left = itemrect.left - HSLIDE;
barrect.right = itemrect.left;
barrect.top = mrectp->top;
barrect.bottom = itemrect.bottom;
textvert = (itemrect.top + itemrect.bottom) / 2 -
(commp->lheight / 2) + commp->finfo.ascent;
/*
* Now draw the item:
* a line demarking the vertical bar;
* a line for empty items, otherwise...
* the check (or other mark) [if any]
* the icon [if any]
* the text of the item
* the clover-key equivalent [if any]
*/
MoveTo(barrect.right - 1, barrect.top);
LineTo(barrect.right - 1, itemrect.top - 1);
TextFace(infop->tstyle);
curhorz = itemrect.left;
if (name[1] == '-') {
MoveTo(curhorz, (itemrect.top + itemrect.bottom) / 2);
LineTo(itemrect.right - 1, (itemrect.top + itemrect.bottom) / 2);
} else {
if (infop->mark) {
MoveTo(curhorz, textvert);
DrawChar(infop->mark);
curhorz += CharWidth(infop->mark);
} else {
curhorz += commp->checkwidth;
}
if (infop->iconnum) {
/*
* Menu icon numbers start at 257.
*/
iconh = GetIcon(((int) infop->iconnum & 0xFF) + 256);
if (iconh) {
iconrect.left = curhorz + ICON_HMARG;
iconrect.top = itemrect.top + ICON_VMARG;
iconrect.right = iconrect.left + ICONSIDE;
iconrect.bottom = iconrect.top + ICONSIDE;
PlotIcon(&iconrect, iconh);
}
curhorz += ICON_HMARG + ICONSIDE + ICON_HMARG;
}
MoveTo(curhorz, textvert);
DrawString(name);
if (infop->keyequiv != '\0') {
MoveTo(itemrect.right -
(commp->cloverwidth + commp->finfo.widMax + RMARG),
textvert);
DrawChar((char) 0x11);
DrawChar(infop->keyequiv);
}
}
/*
* The item is disabled if either the whole menu is disabled
* or the item can be and is disabled.
* (Only the first 31 items in a menu have disable/enable flags.)
*
* If the item is disabled, make it gray.
*/
if (((*menuh)->enableFlags & 1) == 0L || (itemnum < 32 &&
((*menuh)->enableFlags & ((unsigned long) 1 << itemnum)) == 0L)) {
PenMode(patBic);
PenPat(&fuzz);
PaintRect(&itemrect);
}
/*
* Set things up for the next item in the menu.
*/
SetPenState(&oldpen);
itemrect.top = itemrect.bottom;
itemrect.left -= HSLIDE;
}
}
/*
* mchoose() - Find which item (if any) is selected by the given mouse location,
* unhighlight the previously-selected item (if any),
* and highlight the newly-selected item (again, if any).
*/
mchoose(commp, menuh, mrectp, pt, theitemp)
struct commons *commp; /* the common information */
MenuHandle menuh;
Rect *mrectp;
Point pt;
int *theitemp; /* if non-zero, the old item; set it to the new item */
{
char *mcp; /* "Menu Char-Pointer" for traversing the menuData fields */
int itemnum; /* the item number of the current item */
char *name; /* the (Pascal) name of the current item */
struct iteminfo *infop; /* pointer to the current item's info */
Rect itemrect; /* rect enclosing the current item */
Rect barrect; /* rect enclosing the vertical bar for this item */
int wasitem; /* item number of the previously-selected item */
int isitem; /* ditto for the newly-selected item */
/*
* See which item the mouse used to be in (rejecting nonsensical answers)
* and initialize our idea of where the mouse is
* (to be updated if/when we find the true mouse item).
*/
wasitem = *theitemp;
if (wasitem < 0 || wasitem > commp->numitems) {
/*
* We get illegal wasitem numbers when the menu manager tries
* to blink the selected item.
* I haven't found any description of these illegal item numbers.
* If you know how to interpret these numbers, please drop me a note:
* Brad Needham
* 2239 SE 74th Ave.
* Hillsboro, OR 97123 USA
*/
wasitem = 0;
}
isitem = 0;
/*
* Now traverse the menu, looking for the item containing the mouse
* or the item that used to contain the mouse.
*
* These two items are the only ones that will need to be redrawn --
* nothing else has changed appearance.
*/
itemrect.top = mrectp->top + commp->lheight / 2;
itemrect.left = mrectp->left + commp->numitems * HSLIDE;
itemrect.right = mrectp->right;
itemnum = 0;
mcp = (char *) &((*menuh)->menuData);
mcp += ((int) *mcp & 0xFF) + 1;
while (*mcp) {
++itemnum;
name = mcp;
mcp += ((int) *mcp & 0xFF) + 1;
infop = (struct iteminfo *) mcp;
mcp += sizeof(struct iteminfo);
if (infop->iconnum) {
itemrect.bottom = itemrect.top + ICON_VMARG + ICONSIDE + ICON_VMARG;
} else {
itemrect.bottom = itemrect.top + commp->lheight;
}
barrect.left = itemrect.left - HSLIDE;
barrect.right = itemrect.left;
barrect.top = mrectp->top;
barrect.bottom = itemrect.bottom;
/*
* The item is disabled if either the whole menu is disabled
* or the item can be and is disabled.
* (Only the first 31 items in a menu have disable/enable flags.)
*
* If the item is disabled, we can't select it.
*/
if (((*menuh)->enableFlags & 1) == 0L || (itemnum < 32 &&
((*menuh)->enableFlags & ((unsigned long) 1 << itemnum)) == 0L)) {
goto notenabled;
}
/*
* If the mouse is inside this item, then
* this is the newly-selected item.
*/
if (PtInRect(pt, &itemrect) || PtInRect(pt, &barrect)) {
isitem = itemnum;
}
/*
* We want to invert this item if either
* this item is the mouse's item (to be inverted) or
* this item used to be selected (and we want to clear the inversion).
*
* To avoid flicker, we change nothing if the mouse hasn't moved.
*/
if ((itemnum == isitem || itemnum == wasitem) &&
isitem != wasitem) {
InvertRect(&itemrect);
InvertRect(&barrect);
}
notenabled:
itemrect.top = itemrect.bottom;
itemrect.left -= HSLIDE;
}
/*
* Report either the found item,
* or the fact that we found no item.
*/
*theitemp = isitem;
}